package cc.verywell.pureblock.msg.nfs.idx.extraDownload;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import cc.verywell.pureblock.func.node.KeyShell;
import cc.verywell.pureblock.log.Pblog;
import cc.verywell.pureblock.msg.nfs.idx.extraDownload.DloTargetCheck.TargetCheck;
import cc.verywell.pureblock.msg.nfs.strc.NfsStructureV1;

/**
* 
* Copyright © 2020 TanYaqiu. All rights reserved.
* @author TanYaqiu 
* @address flat 601
* @date 2020年2月3日 下午5:17:35
*/
public class DloMap {

	KeyShell mainKey;
	private Map<KeyShell,DloMapWait> map;
	private AtomicInteger mapCount;
	private AtomicLong sizeRest;
	private boolean isInited;
	private long totalSize;
	private DloLog log;
	private DloTarget target;
	private DloTargetCheck targetcheck;
	public DloMap(KeyShell mainKey,DloLog log,DloTarget target) {
		this(mainKey, log, target, Collections.EMPTY_MAP, false, -1);
	}
	public DloMap(KeyShell mainKey,DloLog log,DloTarget target,Map<KeyShell,DloMapWait> map,boolean isInited,long totalSize) {
		this.mainKey = mainKey;
		this.map = new ConcurrentHashMap<KeyShell, DloMapWait>(map);
		this.isInited = isInited;
		this.totalSize = totalSize;
		this.sizeRest = new AtomicLong(isInited?totalSize:-1);
		this.log = log;
		this.target = target;
		if(isInited) {
			this.targetcheck = this.target.useCheck(this);
		}
		this.mapCount = new AtomicInteger(isInited?map.size():1);
	}
	
	private DownloadingMonitor watch;
	void joinWatch(DownloadingMonitor watch) {
		this.watch = watch;
	}
	
	private boolean finished = false;
	public void checkFinish(DloMapWait w) throws IOException{
		int mc;
		if(w!=null)mc = mapCount.decrementAndGet(); //主要是要运行这个;
		else mc = mapCount.get();
		if(mc==0 && this.sizeRest.get()==0){
			mapCount.incrementAndGet();
			synchronized (this) {
				if(mapCount.decrementAndGet()==0) {
					if(finished)return;
					//if(targetcheck!=null)targetcheck.waitFinish();
					if(mapCount.get()==0) {
						finished = true;
						if(targetcheck!=null) {
							//targetcheck.waitFinishAndStop();
							targetcheck.stop();
							if(!map.isEmpty())throw new IOException("not understand why mapCount!=map.size()");
						}
					}
					target.close();
					log.close();
					// 通知监听器完成下载了;
					watch.fireFinished();
				}
			}
		}
	}
	public boolean isFinished() {
		synchronized (this) {
			return finished;
		}
	}

	public DloMapWait get(KeyShell key,NfsStructureV1 s) throws IOException {
		//normal
		DloMapWait w = map.remove(key);
		while(w!=null){
			synchronized (w) {
				if(!w.isRemoved) {
					w.isRemoved=true;
					return w;
				}
			}
			w = map.remove(key);
		}
		//init
		if(!isInited && mainKey.equals(key)) {
			synchronized (this) {
				if(isInited)return null;
				isInited = true;
			}
			long totalsize = s.dataSize();
			w = new DloMapWait();
			if(totalsize>0)
				w.structure_list  = new DltoolIterator<>(new DltoolPosLen(0, totalsize));
			w.isRemoved = true;
			this.isInited = true;
			this.totalSize = totalsize;
			this.sizeRest.set(totalSize);
			log.logWrite_initsize(totalsize);
			//这里开始文件初始化，检查是否够空间等
			target.resetSize(totalsize);
			this.targetcheck = target.useCheck(this);
		}
		return w;
	}
	void add_datastock_by_errcheck(TargetCheck check) {
		add_datastock_by_errcheck(check.pos.len);
		//TODO 到这里为止可以做种了
		watch.fireNewSeedReady(check,totalSize);
	}
	void add_datastock_by_errcheck(long finishedSize) {
		Pblog.DEBUG("download:[",mainKey.key,"] +",finishedSize);
		if(0==this.sizeRest.addAndGet(0-finishedSize)) {
			try {
				checkFinish(null);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		//TODO 统计已写入数量
	}
//	public void add_readonly_req_by_errcheck(KeyShell dkey,DltoolPosLen exportPos) {
//		Pblog.FS("download:[",mainKey.key,"]-[",dkey.key,"] file md5 check wrong at ",exportPos.pos,":",exportPos.len);
//		add_readonly_req(dkey, exportPos);
//	}
	public void add_readonly_req_by_errcheck(KeyShell dkey,DltoolPosLen exportPos) {
		Pblog.FS("download:[",mainKey.key,"]-[",dkey.key,"] file md5 check wrong at ",exportPos.pos,":",exportPos.len);
		///////从map获得旧数据
		DloMapWait dwnew,dw = dwnew =  new DloMapWait();
		dw:while(dw!=null) {
			synchronized (dw) {
				if(dw.isRemoved) {
					dw = dwnew =  new DloMapWait();
					continue dw;
				}
				DloMapWait old = map.putIfAbsent(dkey, dw);
				if(old!=null && old!=dw) {
					dw = old;
					continue dw;
				}
				if(dwnew==dw)mapCount.incrementAndGet();
				/////已确保dw锁定并唯一;
				dw.dataonly_list = DltoolIterator.toNew(dw.dataonly_list, exportPos);//这一小段是结构的
				/////离开dw锁定并唯一;
				break dw;
			}
		}
		if(dwnew==dw)newReq(dkey);
		///////
	}
	private void newReq(KeyShell dkey) {
		//TODO 此处如果不搞异步，会产生迭代，但考虑到文件层级暂时不会太大，算了;
		if(watch!=null)watch.wantList(dkey); //
	}

	public boolean add_structure_req(KeyShell dkey,DltoolIterator<KeyShell> key_history,DltoolIterator<DltoolPosLen> exportPos) {
		///////从map获得旧数据
		DloMapWait dwnew,dw = dwnew =  new DloMapWait();
		dw:while(dw!=null) {
			synchronized (dw) {
				if(dw.isRemoved) {
					dw = dwnew =  new DloMapWait();
					continue dw;
				}
				DloMapWait old = map.putIfAbsent(dkey, dw);
				if(old!=null && old!=dw) {
					dw = old;
					continue dw;
				}
				if(dwnew==dw)mapCount.incrementAndGet();
				/////已确保dw锁定并唯一;
				if(dw.structure_list!=null) { //这一小段是结构的
					DltoolIterator<DltoolPosLen> newPos = dw.structure_list;
					DltoolIterator<DltoolPosLen> pos = exportPos;
					while(pos!=null) {
						newPos = newPos.toNew(pos.t);
						pos = pos.prv;
					}
					dw.structure_list = newPos;
				}else {
					dw.structure_list = exportPos;
				}
				if(key_history!=null) { //这一小段是比较迭代的;
					if(dw.key_history!=null) {
						DltoolIterator<KeyShell> newkh = dw.key_history;
						DltoolIterator<KeyShell> kh = key_history;
						while(kh!=null) {
							DltoolIterator<KeyShell> dwkh = dw.key_history;
							dwkh:{
								while(dwkh!=null) {
									if(kh.t.equals(dwkh.t)) {
										break dwkh;
									}
									dwkh = dwkh.prv;
								}
								newkh = newkh.toNew(kh.t);
							}
							kh = kh.prv;
						}
						dw.key_history = newkh;
					}else {
						dw.key_history = key_history;
					}
				}
				/////离开dw锁定并唯一;
				break dw;
			}
		}
		return (dwnew==dw);//由外面处理newReq(dkey);
		///////
	}
	
	Set<KeyShell> getLoadingList(){
		return map.keySet();
	}

}
